// ==UserScript==
// @name Scratch Comment Redactor
// @namespace https://towerofnix.github.io
// @match *://scratch.mit.edu/*
// @grant none
// ==/UserScript==

const usersSymbol = Symbol()

const replaceText = function(el, newText) {
  while (el.firstChild) {
    el.firstChild.remove()
  }
  el.appendChild(document.createTextNode(newText))
}

const redactCommentThread = function(comment, usernameToRedact) {
  const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

  let topThread = comment
  while (topThread && !topThread.classList.contains('top-level-reply')) {
    topThread = topThread.parentElement
  }

  if (!topThread) {
    console.error('Could not get the thread element, sorry :(')
    console.info(comment)
    return
  }

  if (!(usersSymbol in topThread)) {
    topThread[usersSymbol] = {} // Mapping of username -> descriptor
  }

  const users = topThread[usersSymbol]

  if (!users.hasOwnProperty(usernameToRedact)) {
    const index = Object.keys(users).length
    users[usernameToRedact] = {
      realUsername: usernameToRedact,
      fakeUsername: `[User ${alphabet[index]}]`,
      hue: Math.round(360 / 7 * index)
    }
  }
  const descriptor = users[usernameToRedact]

  const allComments = [comment, ...topThread.querySelectorAll('.reply .comment')]
  for (const comment of allComments) {
    // Redact bold "author" username at top of comment
    const authorUsernameEl = comment.querySelector('.info .name a')
    const authorUsername = authorUsernameEl.innerText.trim()
    if (authorUsername === usernameToRedact) {
      replaceText(authorUsernameEl, descriptor.fakeUsername)

      // Also redact profile picture:
      const authorAvatarEl = comment.querySelector('#comment-user')
      const img = authorAvatarEl.querySelector('img')
      if (img) {
        authorAvatarEl.querySelector('img').remove()
        const fakeProfilePicture = document.createElement('div')
        Object.assign(fakeProfilePicture.style, {
          float: 'left', display: 'block', marginRight: '10px',
          width: '45px', height: '45px',
          backgroundColor: `hsl(${descriptor.hue}deg, 100%, 50%)`
        })
        authorAvatarEl.appendChild(fakeProfilePicture)
      }
    }

    // Redact "@user" replies
    const a = comment.querySelector('.content a')
    if (a && a.innerText.trim() === '@' + usernameToRedact) {
      replaceText(a, '@' + descriptor.fakeUsername)
    }
  }
}

// Button-adder
const commentsContainer = document.querySelector('#comments ul.comments')
if (commentsContainer) {
  const observer = new MutationObserver(mutations => {
    for (const mutation of mutations) {
      for (const addedNode of mutation.addedNodes) {
        if (typeof addedNode.classList === 'undefined') continue
        if (addedNode.classList.contains('top-level-reply') === false) continue
        const topComment = addedNode
        const comments = [topComment, ...topComment.querySelectorAll('.reply .comment')]

        for (const comment of comments) {
          const usernameEl = comment.querySelector('.info .name a')
          if (usernameEl) {
            const actionContainer = comment.querySelector('.actions-wrap')
            const span = document.createElement('span')
            const username = usernameEl.innerText.trim()
            span.classList.add('actions', 'report')
            span.appendChild(document.createTextNode('Anonymize'))
            const handler = () => {
              redactCommentThread(topComment, username)
              replaceText(span, 'Redacted')
              span.style.cursor = 'default'
              span.removeEventListener('click', handler)
            }
            span.addEventListener('click', handler)
            actionContainer.appendChild(span)
          }
        }
      }
    }
  })
  observer.observe(commentsContainer, {childList: true})
}
